package com.ahrhyj.framework.aspectj;

import cn.hutool.core.date.DateTime;
import com.ahrhyj.common.core.redis.RedisCache;
import com.ahrhyj.common.enums.CacheKeyConstants;
import com.ahrhyj.common.annotation.CacheDelete;
import com.ahrhyj.common.annotation.Cacheable;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Aspect
@Component
public class RedisAspect {
    private static final Logger log = LoggerFactory.getLogger(RedisAspect.class);

    private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    @Autowired
    private RedisCache redisCache;

    // 配置织入点
    @Pointcut("@annotation(com.ahrhyj.common.annotation.Cacheable)")
    public void getCachePointCut() {
    }

    @Pointcut("@annotation(com.ahrhyj.common.annotation.CacheDelete)")
    public void delCachePointCut() {
    }


    /**
     * 在所有标注@getCache的地方切入
	 *
      * @param joinPoint
	 * @throws Exception
	 */
    @Around("getCachePointCut()")
    public Object beforeExec(ProceedingJoinPoint joinPoint) throws Exception {
        // 前置：到redis中查询缓存
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();

        Object[] arrayOfObject = joinPoint.getArgs();

        CacheKeyConstants prefix = method.getAnnotation(Cacheable.class).prefix();

        String keyStr = method.getAnnotation(Cacheable.class).key();
        String key = parseExpression(keyStr, method, arrayOfObject);
        // redis中key格式： id
//		String str1 = method.getDeclaringClass().getName();
//		String str2 = method.getName();
        // String key = getCacheKey(str1, str2, arrayOfObject);
        // 获取从redis中查询到的对象
        Object objectFromRedis = redisCache.getCacheObject(generateKey(prefix, key));

        // 如果查询到了
        if (null != objectFromRedis) {
            //System.out.println("从redis中查询到了数据...不需要查询数据库");
            return objectFromRedis;
        }


        // 没有查到，那么查询数据库
        Object object = null;
        try {
            object = joinPoint.proceed();
        } catch (Throwable e) {

            e.printStackTrace();
        }

        int timeOut = method.getAnnotation(Cacheable.class).timeOut();
        redisCache.setCacheObject(generateKey(prefix, key), object, timeOut, TimeUnit.SECONDS);
        // 将查询到的数据返回
        return object;
    }

    @After("delCachePointCut()")
    public void delCache(JoinPoint joinPoint) {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();

        Object[] arrayOfObject = joinPoint.getArgs();

        CacheDelete localCacheDelete = (CacheDelete) method.getAnnotation(CacheDelete.class);
        if (localCacheDelete != null) {
            CacheKeyConstants prefix = method.getAnnotation(CacheDelete.class).prefix();
            for (String str4 : localCacheDelete.key()) {
                try {
                    String localObject4 = parseExpression(str4, method, arrayOfObject);
                    redisCache.deleteObject(generateKey(prefix, localObject4));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getCacheKey(String paramString1, String paramString2,
                               Object[] paramArrayOfObject) {
        StringBuffer localStringBuffer = new StringBuffer();
        localStringBuffer.append(paramString1).append(".").append(paramString2);
        if ((paramArrayOfObject != null) && (paramArrayOfObject.length != 0))
            for (int i = 0; i < paramArrayOfObject.length; i++)
                localStringBuffer.append(".").append(paramArrayOfObject[i]);
        return localStringBuffer.toString();
    }

    /**
     * 生成cache key
     *
     * @param prefix 前缀
     * @param keys   组成key的列表
     * @return prefix_K1_K2..._Kn:
     */
    private String generateKey(CacheKeyConstants prefix, String... keys) {
        String key = prefix.name();
        if (keys != null && keys.length > 0) {
            key += "_" + StringUtils.join(keys, "_");
        }
        return key;
    }

    private String parseExpression(String paramString, Method paramMethod,
                                   Object[] paramArrayOfObject) throws Exception {
        Pattern localPattern1 = Pattern.compile("\\{([0-9]+)\\}");
        Matcher localMatcher1 = localPattern1.matcher(paramString);
        boolean bool1 = localMatcher1.find();
        StringBuffer localStringBuffer1 = new StringBuffer();
        String str1 = "";
        while (bool1) {
            str1 = localMatcher1.group(0);
            int i = NumberUtils.toInt(localMatcher1.group(1), -1);
            if ((i >= 0) && (i < paramArrayOfObject.length))
                localMatcher1.appendReplacement(localStringBuffer1,
                        paramArrayOfObject[i] == null ? ""
                                : paramArrayOfObject[i].toString());
            else
                localMatcher1.appendReplacement(localStringBuffer1, str1);
            bool1 = localMatcher1.find();
        }
        localMatcher1.appendTail(localStringBuffer1);
        str1 = localStringBuffer1.toString();
        Pattern localPattern2 = Pattern.compile("#\\{([a-zA-z_\\.%:]+)\\}");
        Matcher localMatcher2 = localPattern2.matcher(str1);
        boolean bool2 = localMatcher2.find();
        if ((!bool2) || (paramArrayOfObject.length == 0))
            return str1;
        HashMap localHashMap = new HashMap();
        String[] arrayOfString = this.parameterNameDiscoverer
                .getParameterNames(paramMethod);
        for (int j = 0; j < paramArrayOfObject.length; j++)
            localHashMap.put(arrayOfString[j], paramArrayOfObject[j]);
        StringBuffer localStringBuffer2 = new StringBuffer();
        while (bool2) {
            String str2 = localMatcher2.group(1);
            Object localObject = null;
            try {
                if (str2.startsWith("%t:")) {
                    SimpleDateFormat localSimpleDateFormat = new SimpleDateFormat(
                            str2.substring(3));
                    localObject = localSimpleDateFormat.format(new Date());
                } else {
                    localObject = PropertyUtils.getNestedProperty(localHashMap,
                            str2);
                }
            } catch (Exception localException) {
                throw new Exception("Cache advices :: 错误的缓存表达式 " + str1,
                        localException);
            }
            localMatcher2.appendReplacement(localStringBuffer2,
                    localObject != null ? localObject.toString() : str2);
            bool2 = localMatcher2.find();
        }
        localMatcher2.appendTail(localStringBuffer2);
        DateTime dt = new DateTime();
        return localStringBuffer2.toString()
                .replace("%Y", dt.toString("yyyy"))
                .replace("%M", dt.toString("MM"))
                .replace("%d", dt.toString("dd"))
                .replace("%H", dt.toString("HH"))
                .replace("%m", dt.toString("mm"))
                .replace("%s", dt.toString("ss"));
    }
}
